package com.zone.gateway.core.bind;

import com.zone.gateway.core.session.GatewaySession;
import com.zone.gateway.core.mapping.HttpStatement;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.InterfaceMaker;
import org.objectweb.asm.Type;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author: zongzi
 * @description: 代理类工厂
 * @date: 2025/4/17
 */
public class MappedProxyFactory {
    private final String uri;

    // 缓存已经生成的代理类。key = methodName, value = IGenericReference proxy
    private final Map<String, IGenericReference> genericReferenceMap = new ConcurrentHashMap<>();

    public MappedProxyFactory(String uri) {
        this.uri = uri;
    }

    /**
     * 创建代理对象
     *
     * @param gatewaySession 网关会话
     * @return
     */
    public IGenericReference newInstant(GatewaySession gatewaySession) {
        IGenericReference cacheObject = genericReferenceMap.get(uri);
        if (cacheObject != null) {
            return cacheObject;
        }
        HttpStatement httpStatement = gatewaySession.getConfiguration().getHttpStatement(uri);

        // 创建增强器
        Enhancer enhancer = new Enhancer();
        // 设置父类（被代理类）
        enhancer.setSuperclass(Object.class);
        // 增强的回调代理类
        MappedProxy proxy = new MappedProxy(uri, gatewaySession);
        enhancer.setCallback(proxy);
        // 创建动态接口，描述rpc api描述性接口，已sayHi为例
        Type returnType = Type.getType(String.class);
        Type[] argsType = new Type[]{Type.getType(String.class)};
        Signature signature = new Signature(httpStatement.getMethodName(), returnType, argsType);
        InterfaceMaker interfaceMaker = new InterfaceMaker();
        interfaceMaker.add(signature, null);
        Class<?> rpcDescInterface = interfaceMaker.create();
        // 设置接口
        enhancer.setInterfaces(new Class[]{IGenericReference.class, rpcDescInterface});

        // 创建代理对象
        IGenericReference genericReference = (IGenericReference) enhancer.create();
        genericReferenceMap.put(uri, genericReference);
        return genericReference;
    }
}
